/*
 * @Author: 火红色祥云 809880192@qq.com
 * @Date: 2022-06-18 18:31:56
 * @LastEditors: 火红色祥云 809880192@qq.com
 * @LastEditTime: 2022-11-08 19:07:42
 * @FilePath: /imx6ull-app/06_beep/06_dtsbeep.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/types.h>

#include <linux/of.h>

#include <linux/of_gpio.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/kernel.h>
#include <linux/ide.h>

#define DTSBEEPNAME "chr_dtsbeep"   //设备名称 /proc/devices
#define CHRDEVCNT   1               //一个字符设备
#define DEVNODENAME "chr_beepdevnode"///dev下的设备节点名称

struct ST_BEEP
{
    //初始化GPIO
    struct device_node *st_beep_node;
    int beep_num;

    //挂在字符设备
    dev_t devid;
    struct cdev st_beep_cdev;
    //创建设备节点
    struct class *st_beep_class;
    struct device *st_beep_device;
};

struct ST_BEEP st_beep;

int beep_open(struct inode *nd, struct file *fp)
{
    fp->private_data = &st_beep;
    return 0;
}
ssize_t beep_read(struct file *fp, char __user *buf, size_t len, loff_t *offset)
{
    return 0;
}

ssize_t beep_write(struct file *fp, const char __user *buf, size_t len, loff_t *offset)
{
    int ret_val;

    unsigned char user_databuf[10];

    struct ST_BEEP *st_private = fp->private_data;

    ret_val = copy_from_user(user_databuf,buf,len);
    if (ret_val < 0)
    {
        printk("data from  user failed\r\n");
        return -1;
    }
    
   // printk("Recv:%d\r\n",user_databuf[0]);
    if ((user_databuf[0] == 1)||(user_databuf[0] == '1'))
    {
        gpio_set_value(st_private->beep_num,1);
    }
    else if((user_databuf[0] == 0)||(user_databuf[0] == '0'))
    {
        gpio_set_value(st_private->beep_num,0);
    }
    
    return 0;
}

int beep_release(struct inode *nd, struct file *fp)
{
    return 0;
}

static struct file_operations fops_beep = 
{
    .owner = THIS_MODULE,
    .open = beep_open,
    .write = beep_write,
    .read = beep_read,
    .release = beep_release,
};

static int __init beep_init(void)
{
    struct property *st_beep_property;
    const char *str;
    int ret_val;
    //查找beep的设备节点struct device_node *of_find_node_by_path(const char *path)
    st_beep.st_beep_node = of_find_node_by_path("/lux_beep");
    if (st_beep.st_beep_node == NULL)
    {
        printk("BeepNode is not find\r\n");
        return -1;
    }

    //打印compatible属性值struct property *of_find_property(const struct device_node *np,const char*name,int *lenp)
    st_beep_property = of_find_property(st_beep.st_beep_node,"compatible",NULL);
    if (st_beep_property == NULL)           
    {
        printk("st_beep_property not found\r\n");
        return -1;
    }
    else
    {
        printk("st_beep_property found:%s\r\n",(char *)st_beep_property->value);
    }
    
    //读取字符串属性 
    /*
    int of_property_read_string(struct device_node *np,
					  const char *propname,
					  const char **out_string)
    */
    ret_val = of_property_read_string(st_beep.st_beep_node,"status",&str);
    if (ret_val < 0)
    {
        printk("read failed\r\n");
    }
    else
    {
        printk("statue:%s\r\n",str);
    }
    
    //获取gpio的id
    /*
    int of_get_named_gpio(struct device_node *np,
                                   const char *propname, int index)
    */
    st_beep.beep_num = of_get_named_gpio(st_beep.st_beep_node,"beep-gpio",0);
    if (st_beep.beep_num < 0)
    {
        printk("beep_num is not request\r\n");
    }
    else
    {
        printk("bee_num:%d\r\n",st_beep.beep_num);
    }

    //设置为输出
    ret_val = gpio_direction_output(st_beep.beep_num,1);
    if (ret_val < 0)
    {
        printk("can`t set output\r\n");
    }
    
    //设备号int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
    alloc_chrdev_region(&st_beep.devid,0,CHRDEVCNT,DTSBEEPNAME);

    st_beep.st_beep_cdev.owner = THIS_MODULE;
    //挂载字符设备void cdev_init(struct cdev *, const struct file_operations *);
    cdev_init(&st_beep.st_beep_cdev,&fops_beep);
    //int cdev_add(struct cdev *, dev_t, unsigned);
    cdev_add(&st_beep.st_beep_cdev,st_beep.devid,CHRDEVCNT);

    //在/dev下创建设备节点
    /*
    struct class *class_create(owner, name)
    */
    st_beep.st_beep_class = class_create(THIS_MODULE,DTSBEEPNAME);

    /*
    struct device *device_create(struct class *cls, struct device *parent,
			     dev_t devt, void *drvdata,
			     const char *fmt, ...);
    */
    st_beep.st_beep_device = device_create(st_beep.st_beep_class,NULL,st_beep.devid,NULL,DEVNODENAME);


    return 0;
}

static void __exit beep_exit(void)
{
    //void cdev_del(struct cdev *);
    cdev_del(&st_beep.st_beep_cdev);
    unregister_chrdev_region(st_beep.devid,1);
    device_destroy(st_beep.st_beep_class,st_beep.devid);
    class_destroy(st_beep.st_beep_class);
}

module_init(beep_init);
module_exit(beep_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lux");